﻿using Microsoft.Xaml.Behaviors;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

/*
 * 代码：https://gist.github.com/BYJRK/37d6df1c9f1d198f73538c5167d50f6a
 * 介绍：https://www.bilibili.com/video/BV1E14y137qd/?spm_id_from=pageDriver&vd_source=9b597a6bfcb0f5764169606fb5cd6e6a
 */
namespace WpfTutorials.RadioButtonBindings.Behaviors;

/// <summary>
/// Allows a <see cref="Panel"/> or <see cref="ItemsControl"/> to have a <see cref="ComboBox"/>-like behavior
/// that has a <see cref="SelectedItem"/> dependency property to be bound to a property in view model. <para/>
/// [行为] 给包含一系列 RadioButton 的 Panel 或 ItemsControl 引入一个可绑定的 SelectedItem 属性（RadioButton 的值需存放在 Tag 中）
/// </summary>
/// <example>
/// <code lang="xaml">
/// <![CDATA[
/// xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
/// xmlns:be="clr-namespace:WpfTutorials.RadioButtonBindings.Behaviors"
/// 
/// <StackPanel>
///     <i:Interaction.Behaviors>
///         <be:SelectedItemBehavior SelectedItem="{Binding SelectedItem}" />
///     </i:Interaction.Behaviors>
///     <RadioButton Tag="A" />
///     <RadioButton Tag="B" />
///     <RadioButton Tag="C" />
/// </StackPanel>
/// ]]>
/// </code>
/// </example>
public class SelectedItemBehavior : Behavior<FrameworkElement>
{
    #region SelectedItem Dependency Property

    public object SelectedItem
    {
        get => GetValue(SelectedItemProperty);
        set => SetValue(SelectedItemProperty, value);
    }

    /// <summary>
    /// The <see cref="SelectedItem"/> within this <see cref="Panel"/> or <see cref="ItemsControl"/>
    /// </summary>
    public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
        nameof(SelectedItem),
        typeof(object),
        typeof(SelectedItemBehavior),
        new PropertyMetadata(null, OnSelectedItemChanged)
    );

    private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var behavior = Interaction.GetBehaviors(d).OfType<SelectedItemBehavior>().FirstOrDefault();
        if (behavior is null) return;
        foreach (var child in behavior.Children)
        {
            child.IsChecked = child.Tag.Equals(e.NewValue);
        }
    }

    #endregion

    protected override void OnAttached()
    {
        AssociatedObject.Loaded += OnAssociatedLoaded;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.Loaded -= OnAssociatedLoaded;
        foreach (var child in Children)
        {
            child.Checked -= OnRadioButtonChecked;
        }
    }

    private void OnAssociatedLoaded(object? sender, RoutedEventArgs e)
    {
        foreach (var child in Children)
        {
            child.Checked += OnRadioButtonChecked;
            child.IsChecked = child.Tag.Equals(SelectedItem);
        }
    }

    private void OnRadioButtonChecked(object sender, RoutedEventArgs e)
    {
        var radio = sender as RadioButton;
        SelectedItem = radio!.Tag;
        BindingOperations.GetBindingExpression(this, SelectedItemProperty)?.UpdateSource();
    }

    /// <summary>
    /// Enumerate all <see cref="RadioButton"/> children of the <see cref="Behavior{T}.AssociatedObject"/>
    /// </summary>
    private IEnumerable<RadioButton> Children => AssociatedObject switch
    {
        Panel panel => panel.Children.OfType<RadioButton>(),
        ItemsControl control => control.Items.OfType<RadioButton>(),
        _ => Enumerable.Empty<RadioButton>(),
    };
}
